package com.config.globalException;

import com.common.Result;
import com.common.ResultEnum;
import com.config.captcha.CaptchaException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.stream.Collectors;

/**
 * 全局异常捕获，@RestControllerAdvice + @ExceptionHandler
 * <p>
 * 不一定必须在 controller 层本身抛出异常才能被 GlobalExceptionHandler 处理，只要异常最后是从 contoller 层抛出去的就可以被全局异常处理器处理。
 * 异步方法中的异常不会被全局异常处理。
 * 抛出的异常如果被代码内的 try/catch 捕获了，就不会被 GlobalExceptionHandler 处理了。
 * </p>
 * @since 2022-1-20 23:39:24
 * @version 1.0
 * @author clay
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理 Exception 异常
     *
     * @param httpServletRequest httpServletRequest
     * @param e                  异常
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = {Exception.class,RuntimeException.class})
    public Result exceptionHandler(HttpServletRequest httpServletRequest, Exception e) {
        logger.error("服务错误:", e);
        return Result.fail("服务出错500，请联系开发者紧急修复o(*￣︶￣*)o");
    }

    @ResponseBody
    @ExceptionHandler(value = BusinessException.class)
    public Result exceptionHandler(HttpServletRequest httpServletRequest, BusinessException e) {
        logger.error("BusinessException异常:", e);
        return Result.fail(e.getCode(), e.getMsg());
    }

    //权限不足异常处理
    @ResponseBody
    @ExceptionHandler(value = AccessDeniedException.class)
    public Result accessDeniedExceptionHandler(HttpServletRequest httpServletRequest, AccessDeniedException e) {
        logger.error("BusinessException异常:", e);
        return Result.fail(ResultEnum.FORBID.getCode(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = CaptchaException.class)
    public Result captchaException(HttpServletRequest httpServletRequest, CaptchaException e) {
        logger.error("CaptchaException:", e);
        return Result.fail(e.getMessage());
    }

//    @ResponseBody
//    @ExceptionHandler(value = MethodArgumentNotValidException.class)
//    public Result exceptionHandler(HttpServletRequest httpServletRequest, MethodArgumentNotValidException e) {
//
//        logger.error("MethodArgumentNotValidException:", e);
//                    return Result.fail(500, e.getBindingResult().getAllErrors().stream()
//                    .map(ObjectError::getDefaultMessage)
//                    .collect(Collectors.joining("; ")));
//    }

//    @ResponseStatus(HttpStatus.BAD_REQUEST)
//    @ExceptionHandler(value = RuntimeException.class)
//    public Result handler(RuntimeException e) {
//        return Result.fail("服务出错500，请联系开发者紧急修复o(*￣︶￣*)o");
//    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalAccessException.class)
    public Result handler(IllegalAccessException e) {
        logger.error("IllegalAccessException异常被捕获: {}", e.getMessage());
        return Result.fail(e.getMessage());
    }

//    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
    public Result handler(Exception e) {
        System.out.println(e.getClass().getName());
        if (e instanceof MethodArgumentNotValidException) {
            // BeanValidation exception
            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
            return Result.fail(500, ex.getBindingResult().getAllErrors().stream()
                    .map(ObjectError::getDefaultMessage)
                    .collect(Collectors.joining("; "))
            );
        } else if (e instanceof ConstraintViolationException) {
            // BeanValidation GET simple param
            ConstraintViolationException ex = (ConstraintViolationException) e;
            return Result.fail(500,
                    ex.getConstraintViolations().stream()
                            .map(ConstraintViolation::getMessage)
                            .collect(Collectors.joining("; "))
            );
        } else if (e instanceof BindException) {
            // BeanValidation GET object param
            BindException ex = (BindException) e;
            return Result.fail(500,
                    ex.getAllErrors().stream()
                            .map(ObjectError::getDefaultMessage)
                            .collect(Collectors.joining("; "))
            );
        }

        return Result.fail(e.getMessage());
    }

}
